Spring笔记(五)—— IOC 容器之依赖注入

Spring 支持两种依赖注入方式,分别为构造函数注入和属性注入,此外还支持工厂方法注入方式。

构造函数注入

构造函数注入由容器调用带参的构造函数来完成,与通过调用指定参数的静态工厂方法来构造 bean 相似。包含构造函数的类是一个没有依赖于容器指定的接口,基类或注释的 POJO,如下所示:

1
2
3
4
5
6
7
8
9
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}

构造函数参数解析

使用参数的类型进行构造函数参数解析匹配。如果 bean 定义的构造函数参数不存在歧义,那么构造函数参数在 <beans/> 中定义的参数顺序和 bean 被实例化时提供给适当的构造函数的参数顺序一致。

1
2
3
4
5
6
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}

1
2
3
4
5
6
7
8
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>

按类型匹配入参

<constructor-arg> 的元素中有一个 type 属性,它为 Spring 提供了判断配置项和构造函数入参对应关系的信息。Spring 的配置文件采用和元素标签顺序无关的策略,在一定程度上保证配置信息的正确性,<constructor-arg> 位置的改变并不会对最终的配置产生影响。

1
2
3
4
5
6
7
8
9
10
11
12
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>

按索引匹配入参

如果构造函数两个入参的类型相同,仅通过 type 无法确定对应关系,需要通过入参索引的方式进行确定,参数和构造函数参数需要顺序对应。

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>

按参数名匹配入参

使用 @ConstructorProperties 注解指定入参名,在 xml 文件中通过参数名的方式匹配入参。

1
2
3
4
5
6
7
8
9
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>

属性注入

属性注入即通过 setXxx() 方法注入 Bean 的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常见的注入方式。属性注入要求 Bean 提供一个默认的构造函数,并为需要注入的属性提供对应的 Setter 方法。Spring 先调用 Bean 的默认构造函数实例化 Bean 对象,然后通过反射的方式调用 Setter 方法注入属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public void setYears(int years) {
this.years = years;
}
public void setUltimateAnswer(int ultimateAnswer) {
this.ultimateAnswer = ultimateAnswer;
}
}

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<property name="years"><value>7500000</value></property>
<property name="ultimateAnswer"><value>42</value></property>
</bean>

注入参数详解

字面值

字面值指可用字符串表示的值,且这些值可通过 value 属性进行注入。Spring 容器在内部为字面值提供了编辑器,它可以将以字符串表示的字面值转换为内部变量的相应类型。

1
2
3
4
5
6
7
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>

使用 p-namespace 简化上述的 xml 配置:

1
2
3
4
5
6
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>

引用其他 bean

使用 ref 指定引用的 bean:

1
2
3
4
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>

1
2
3
4
5
6
7
8
<!-- in the child (descendant) context -->
<!-- bean name is the same as the parent bean -->
<bean id="accountService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>

内部 Bean

内部 Bean 和 Java 代码中匿名内部类相似,没有名字,也不能被其他 Bean 引用,只能在声明出为外部 Bean 提供实例注入。内部 Bean 即使提供了 id、name、scope 属性,也会被忽略,scope 默认为 prototype类型。

1
2
3
4
5
6
7
8
9
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>

集合类型属性

, , , 和 元素中,可以设置属性和 Java 集合类型 List, Set, Map 和 Properties 的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>

集合合并

允许子 <bean> 继承父 <bean> 的同名属性集合元素,并将子 <bean> 中配置的集合属性值和父 <bean> 中配置的同名属性值合并起来作为最终 Bean 的属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>

强类型集合

JDK5.0 提供了强类型集合的新功能,允许为集合元素指定类型。

1
2
3
4
5
6
public class Foo {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}

1
2
3
4
5
6
7
8
9
10
11
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>

null 值和空 string 值

参数值为空 String:

1
2
3
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>

参数值为 null:

1
2
3
4
5
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>

简化配置方式

Spring 为字面值、引用 Bean 和集合都提供了简化的配置方式。

使用 p-namespace

1
2
3
4
5
6
7
8
9
10
11
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="foo@bar.com"/>
</beans>

使用 c-namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
<!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean>
<!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>
</beans>

级联属性

1
2
3
<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>

foo bean 有一个 fred 属性,在 fred 下有一个 bob 属性, 在 bob 下有一个 sammy 属性。

参考资料:

Spring 3.x 企业应用开发实战
Spring Framework Reference Documentation

热评文章